<?php
/**
 * Created by PhpStorm.
 * User: CY
 * Date: 2018/3/23
 * Time: 16:15
 */

namespace app\api\service;

use app\api\model\OrderProduct;
use app\api\model\Product;
use app\api\model\UserAddress;
use app\lib\enum\OrderStatusEnum;
use app\lib\exception\OrderException;
use app\lib\exception\UserException;
use app\api\model\Order as OrderModel;
use think\Exception;

/**
 * 订单类
 * 订单做了以下简化
 * 创建订单时会检测库存量，但并不会扣除库存量，因为这需要队列支持
 * 未支付的订单再次支付时可能会出现库存不足的情况，所以本项目采用3次检测
 * 1.创建订单时检测库存
 * 2.支付前检测库存
 * 3.支付成功后检测库存
 * Class Order
 * @package app\api\service
 */
class Order
{
    protected $oProducts; // 下订单提交过来的商品列表
    protected $products; // 根据订单查找到的真实商品信息
    protected $uid;

    function __construct() {
    }

    /**
     * 用户下单
     * @param $uid
     * @param $oProducts
     * @return array
     */
    public function place($uid,$oProducts){
        $this->oProducts = $oProducts;
        $this->products = $this->getProductsByOrder($oProducts);
        $this->uid = $uid;
        // 整理完成后的订单信息
        $status = $this->getOrderStatus();
        if(!$status['pass']){
            $status['order_id'] = -1;
            return $status;
        }

        $orderSnap = $this->snapOrder();
        $status = $this->createOrderByTrans($orderSnap);
        $status['pass'] = true;
        return $status;
    }
    
    /**
     * 根据订单检测商品库存
     * @param $orderID
     * @return array
     */
    public function checkOrderStock($orderID){
        $oProducts = OrderProduct::where('order_id','=',$orderID)->select();
        $this->products = $this->getProductsByOrder($oProducts);
        $this->oProducts = $oProducts;
        $status = $this->getOrderStatus();
        return $status;
    }

    /**
     * 创建订单
     * @param $snap 订单快照
     * @return array
     * @throws Exception
     */
    private function createOrderByTrans($snap){
        try {
            // 订单号
            $orderNo = self::makeOrderNo();
            $order = new OrderModel();
            $order->user_id = $this->uid;
            $order->order_no = $orderNo;
            $order->total_price = $snap['orderPrice'];
            $order->total_count = $snap['totalCount'];
            $order->snap_img = $snap['snapImg'];
            $order->snap_name = $snap['snapName'];
            $order->snap_address = $snap['snapAddress'];
            $order->snap_items = $snap['pStatus'];
            $order->save();

            $orderID = $order->id;
            $create_time = $order->create_time;

            foreach ($this->oProducts as &$p){
                $p['order_id'] = $orderID;
            }

            $orderProduct = new OrderProduct();
            $orderProduct->saveAll($this->oProducts);
            return [
                'order_no' => $orderNo,
                'order_id' => $orderID,
                'create_time' => $create_time
            ];

        }catch (Exception $e){
            throw $e;
        }
    }

    /**
     * 根据订单来查找真实商品
     * @param $oProducts
     * @return mixed
     */
    private function getProductsByOrder($oProducts){
        $oPIDs = [];
        foreach ($oProducts as $item){
            $oPIDs[] = $item['product_id'];
        }
        $products = Product::all($oPIDs)
            ->visible(['id','price','stock','name','main_img_url'])
            ->toArray();
        return $products;
    }
    
    /**
     * 整理整个订单信息，包括商品列表
     * @return array
     */
    private function getOrderStatus(){
        $status = [
            'pass' => true,
            'orderPrice' => 0,
            'pStatusArray' => []
        ];
        
        foreach ($this->oProducts as $oProduct){
            $pStatus = $this->getProductStatus($oProduct['product_id'],$oProduct['count'],$this->products);
            if(!$pStatus['haveStock']){
                $status['pass'] = false;
            }
            $status['orderPrice'] += $pStatus['totalPrice'];
            $status['pStatusArray'][] = $pStatus;
        }
        return $status;
    }
    
    /**
     * 根据传入的商品id和从库里查出的商品信息，整理单个商品信息
     * @param $oPID 订单中的商品id
     * @param $oCount 订单中的商品数量
     * @param $products 从库中查出来的商品信息
     * @return array
     * @throws OrderException
     */
    private function getProductStatus($oPID,$oCount,$products){
        $pIndex = -1;
        // 初始化具体的商品信息
        $pStatus = [
            'id' => null,
            'haveStock' => false,
            'count' => 0,
            'name' => '',
            'totalPrice' => 0
        ];
        
        foreach ($products as $k => $product){
            if($product['id'] == $oPID){
                $pIndex = $k;
            }
        }
        
        if($pIndex == -1){
            // 客户端传递的product_id有可能是错误的
            throw new OrderException([
                'msg' => 'id为' . $oPID . '的商品不存在，订单创建失败'
            ]);
        }else{
            $product = $products[$pIndex];
            $pStatus['id'] = $product['id'];
            $pStatus['name'] = $product['name'];
            $pStatus['count'] = $oCount;
            $pStatus['totalPrice'] = $product['price'] * $oCount;
            // 判断库存
            if($product['stock'] - $oCount >= 0){
                $pStatus['haveStock'] = true;
            }
        }
        
        return $pStatus;
    }

    /**
     * 获得用户收货地址
     * @return array|false|\PDOStatement|string|\think\Model
     * @throws UserException
     */
    private function getUserAddress(){
        $userAddress = UserAddress::where('user_id','=',$this->uid)->find();
        if(!$userAddress){
            throw new UserException([
                'msg' => '用户收货地址不存在，下单失败'
            ]);
        }
        return $userAddress;
    }

    /**
     * 订单快照
     * @return array
     */
    private function snapOrder(){
        // status可以单独定义一个类
        $snap = [
            'orderPrice' => 0,
            'totalCount' => 0,
            'pStatus' => [],
            'snapAddress' => json_encode($this->getUserAddress()),
            'snapName' => $this->products[0]['name'],
            'snapImg' => $this->products[0]['main_img_url']
        ];

        if(count($this->products) > 1){
            $snap['snapName'] .= '等';
        }

        foreach ($this->products as $key => $product){
            $oProduct = $this->oProducts[$key];
            $pStatus = $this->snapProduct($product,$oProduct['count']);
            $snap['orderPrice'] += $pStatus['totalPrice'];
            $snap['totalCount'] += $pStatus['count'];
            $snap['pStatus'][] = $pStatus;
        }
        return $snap;
    }
    
    /**
     * 生成单个商品订单快照
     * @param $product 下单时候从数据库中查出的商品
     * @param $oCount 下单的商品数量
     * @return array
     */
    private function snapProduct($product,$oCount){
        $pStatus = [
            'id' => null,
            'name' => null,
            'main_img_url' => null,
            'count' => $oCount,
            'totalPrice' => 0,
            'price' => 0
        ];
        
        // 以服务器价格为准，生成订单
        $pStatus['totalPrice'] = $oCount * $product['price'];
        $pStatus['name'] = $product['name'];
        $pStatus['id'] = $product['id'];
        $pStatus['main_img_url'] = $product['main_img_url'];
        $pStatus['price'] = $product['price'];
        return $pStatus;
    }

    /**
     * 按照规则生成订单号
     * @return string
     */
    public static function makeOrderNo(){
        $yCode = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
        $orderSn = $yCode[intval(date('Y')) - 2018] . strtoupper(dechex(date('m'))) . date('d') . substr(time(),-5) . substr(microtime(),2,5) . sprintf('%02d',rand(0,99));
        return $orderSn;
    }

    /**
     * 根据订单发货，并发送模板消息
     * @param $orderID
     * @param string $jumpPage
     * @return bool
     * @throws OrderException
     */
    public function delivery($orderID,$jumpPage = ''){
        $order = OrderModel::where('id','=',$orderID)->find();
        if(!$order){
            throw new OrderException();
        }
        if($order->status != OrderStatusEnum::PAID){
            throw new OrderException([
                'msg' => '该订单暂未付款或已更新订单',
                'errorCode' => 80002,
                'code' => 403
            ]);
        }
        $order->status = OrderStatusEnum::DELIVERED;
        $order->save();

        $message = new DeliveryMessage();
        return $message->sendDeliveryMessage($order,$jumpPage);

    }
}